package com.sunwayworld.basemodule.rm.hospital.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sunwayworld.basemodule.rm.hospital.bean.*;
import com.sunwayworld.basemodule.rm.hospital.dao.LimsHospitalInterfaceDao;
import com.sunwayworld.basemodule.rm.hospital.service.LimsHospitalInterfaceParamsService;
import com.sunwayworld.basemodule.rm.hospital.service.LimsHospitalInterfaceService;
import com.sunwayworld.basemodule.rm.hospital.service.LimsInterfaceSourceService;
import com.sunwayworld.cloud.lims.reporttemplate.service.LimsReportTemplateSourceService;
import com.sunwayworld.cloud.lims.reporttemplate.support.constant.LimsSourceDataTypeEnum;
import com.sunwayworld.cloud.lims.reporttemplate.support.data.LimsTemplateDataList;
import com.sunwayworld.cloud.lims.reporttemplate.support.data.LimsTemplateDataMap;
import com.sunwayworld.cloud.lims.reporttemplate.support.helper.LimsSqlHelper;
import com.sunwayworld.cloud.module.lcdp.apiintegration.support.LcdpApiClientHelper;
import com.sunwayworld.framework.at.annotation.AuditTrailEntry;
import com.sunwayworld.framework.at.annotation.AuditTrailType;
import com.sunwayworld.framework.context.ApplicationContextHelper;
import com.sunwayworld.framework.exception.InvalidDataException;
import com.sunwayworld.framework.mybatis.mapper.MapperParameter;
import com.sunwayworld.framework.mybatis.mapper.MatchPattern;
import com.sunwayworld.framework.mybatis.mapper.SearchFilter;
import com.sunwayworld.framework.restful.data.RestJsonWrapperBean;
import com.sunwayworld.framework.spring.annotation.GikamBean;
import com.sunwayworld.framework.support.tree.TreeDescriptor;
import com.sunwayworld.framework.support.tree.TreeHelper;
import com.sunwayworld.framework.support.tree.bean.AbstractTreeNode;
import com.sunwayworld.framework.utils.ArithUtils;
import com.sunwayworld.framework.utils.CollectionUtils;
import com.sunwayworld.framework.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Repository
@GikamBean
public class LimsHospitalInterfaceServiceImpl implements LimsHospitalInterfaceService {

    private static final Logger log = LoggerFactory.getLogger(LimsHospitalInterfaceServiceImpl.class);

    @Autowired
    private LimsHospitalInterfaceDao limsHospitalInterfaceDao;

    @Lazy
    @Autowired
    private LimsInterfaceSourceService sourceService;

    @Lazy
    @Autowired
    private LimsHospitalInterfaceParamsService paramsService;

    @Lazy
    @Autowired
    private LimsReportTemplateSourceService reportTemplateSourceService;

    @Override
    @SuppressWarnings("unchecked")
    public LimsHospitalInterfaceDao getDao() {
        return limsHospitalInterfaceDao;
    }

    @Override
    @Transactional
    @AuditTrailEntry(AuditTrailType.INSERT)
    public Long insert(RestJsonWrapperBean jsonWrapper) {
        LimsHospitalInterfaceBean limsHospitalInterface = jsonWrapper.parseUnique(LimsHospitalInterfaceBean.class);
        limsHospitalInterface.setId(ApplicationContextHelper.getNextIdentity());
        getDao().insert(limsHospitalInterface);
        return limsHospitalInterface.getId();
    }

    @Override
    public void testApi(RestJsonWrapperBean wrapperBean) {
        String id = wrapperBean.getParamValue("id");
        MapperParameter parameter = wrapperBean.extractMapFilter();
        LimsHospitalInterfaceBean interfaceBean = selectById(Long.parseLong(id));
        List<LimsInterfaceSourceBean> interfaceSourceBeans = sourceService.selectListByFilter(SearchFilter.instance()
                .match("interfaceId", interfaceBean.getId()).filter(MatchPattern.EQ));
        List<LimsHospitalInterfaceParamsBean> paramsBeans = paramsService.selectListByFilter(SearchFilter.instance()
                .match("interfaceId", interfaceBean.getId()).filter(MatchPattern.EQ));
        LimsTemplateDataMap<Object> resolve = resolve(interfaceSourceBeans, parameter); // 解析接口数据源为 map对象
        if ("Restful".equalsIgnoreCase(interfaceBean.getApiType())) {
            restful(interfaceBean, paramsBeans, parameter, resolve);
        } else {

        }
    }

    private void restful(LimsHospitalInterfaceBean interfaceBean, List<LimsHospitalInterfaceParamsBean> paramsBeans, MapperParameter rootParameter, LimsTemplateDataMap<Object> resolve){
        log.info(JSONObject.toJSONString(resolve));
        Map<String, Object> params = paramsBeans.stream().filter(p -> "params".equalsIgnoreCase(p.getCategory()))
                .collect(Collectors.toMap(LimsHospitalInterfaceParamsBean::getParam, p -> filterValue(p, rootParameter, resolve)));

        Map<String, String> header = paramsBeans.stream().filter(p -> "headers".equalsIgnoreCase(p.getCategory()))
                .collect(Collectors.toMap(LimsHospitalInterfaceParamsBean::getParam, p -> filterValue(p, rootParameter, resolve).toString()));

        List<LimsHospitalInterfaceParamsBean> bodyList = paramsBeans.stream().filter(p -> "body".equalsIgnoreCase(p.getCategory())).collect(Collectors.toList());
        String body = null;
        if (!CollectionUtils.isEmpty(bodyList)) {
            JSONObject bodyJson = new JSONObject();
            if (bodyList.size() == 1 && "sql".equalsIgnoreCase(bodyList.get(0).getType())) {
                bodyJson = this.parseBodyValue(new LimsHospitalInterfaceParamsTreeDTO(bodyList.get(0)), rootParameter, resolve).getJSONObject(bodyList.get(0).getParam());
            } else {
                TreeDescriptor<LimsHospitalInterfaceParamsBean> treeDescriptor = new TreeDescriptor("param", "parentParam", "param");
                List<LimsHospitalInterfaceParamsTreeDTO> sourceTreeDTOList = TreeHelper.parseTreeNode(bodyList, treeDescriptor, LimsHospitalInterfaceParamsTreeDTO.class);
                for (LimsHospitalInterfaceParamsTreeDTO sourceTreeDTO : sourceTreeDTOList) {
                    JSONObject value = this.parseBodyValue(sourceTreeDTO, rootParameter, resolve);
                    bodyJson.put(sourceTreeDTO.getParam(), value);
                }
            }
            body = bodyJson.toJSONString();
        }
        String s = LcdpApiClientHelper.sendHttp(interfaceBean.getApiCode(), params, header, body);

    }

    private JSONObject parseBodyValue(LimsHospitalInterfaceParamsTreeDTO sourceTreeDTO, MapperParameter rootParameter, LimsTemplateDataMap<Object> resolve) {
        JSONObject json = new JSONObject();
        Object value = filterValueTree(sourceTreeDTO, rootParameter, resolve);
        json.put(sourceTreeDTO.getParam(), value);
        List<AbstractTreeNode> children = sourceTreeDTO.getChildren();
        if (!CollectionUtils.isEmpty(children)) {
            children.forEach((childNode) -> {
                LimsHospitalInterfaceParamsTreeDTO childSourceDTO = (LimsHospitalInterfaceParamsTreeDTO)childNode;
                JSONObject child = this.parseBodyValue(childSourceDTO, rootParameter, resolve);
                if (child != null) {
                    json.put(childSourceDTO.getParam(), child);
                }
            });
        }
        return json;
    }

    private Object filterValueTree(LimsHospitalInterfaceParamsTreeDTO paramsBean , MapperParameter rootParameter, LimsTemplateDataMap<Object> resolve) {
        String value = paramsBean.getValue();
        if ("param".equalsIgnoreCase(paramsBean.getType())) {
            return rootParameter.get(value);
        } else if ("sql".equalsIgnoreCase(paramsBean.getType())) {
            String[] split = value.split("\\.");
            int length = split.length;
            if (length == 1) return resolve.get(split[0]);

            LimsTemplateDataMap<Object> sub = resolve;
            for (int i = 0; i < length; i++) {
                if (sub == null) throw new InvalidDataException("参数维护错误！");
                if (length == i + 1) return sub.get(split[i]);
                sub = sub.get(split[i]);
            }
        }
        return value;
    }

    private Object filterValue(LimsHospitalInterfaceParamsBean paramsBean , MapperParameter rootParameter, LimsTemplateDataMap<Object> resolve) {
        String value = paramsBean.getValue();
        if ("param".equalsIgnoreCase(paramsBean.getType())) {
            return rootParameter.get(value);
        } else if ("sql".equalsIgnoreCase(paramsBean.getType())) {
            String[] split = value.split("\\.");
            int length = split.length;
            if (length == 1) return resolve.get(split[0]);

            LimsTemplateDataMap<Object> sub = resolve;
            for (int i = 0; i < length; i++) {
                if (sub == null) throw new InvalidDataException("参数维护错误！");
                if (length == i + 1) return sub.get(split[i]);
                sub = sub.get(split[i]);
            }
        }
        return value;
    }

    public LimsTemplateDataMap<Object> resolve(List<LimsInterfaceSourceBean> sourceList, MapperParameter rootParameter) {
        LimsTemplateDataMap<Object> paramMap = new LimsTemplateDataMap();
        if (!CollectionUtils.isEmpty(sourceList)) {
            TreeDescriptor<LimsInterfaceSourceBean> treeDescriptor = new TreeDescriptor("sourceCode", "parentSourceCode", "sourceName");
            List<LimsInterfaceSourceTreeDTO> sourceTreeDTOList = TreeHelper.parseTreeNode(sourceList, treeDescriptor, LimsInterfaceSourceTreeDTO.class);
            sourceTreeDTOList.forEach((sourceTreeDTO) -> {
                List<LimsTemplateDataMap<Object>> dataParam = this.execute(sourceTreeDTO, new MapperParameter(), rootParameter);
                putParam(paramMap, sourceTreeDTO, dataParam);
            });
        }
        return paramMap;
    }

    private List<LimsTemplateDataMap<Object>> execute(LimsInterfaceSourceTreeDTO sourceTreeDTO, MapperParameter param, MapperParameter rootParameter) {
        String sql = sourceTreeDTO.getSql();
        LimsTemplateDataList<LimsTemplateDataMap<Object>> dataMapList = new LimsTemplateDataList();
        if (!StringUtils.isEmpty(sql)) {
            RestJsonWrapperBean wrapperBean = new RestJsonWrapperBean();
            if (!StringUtils.isEmpty(sourceTreeDTO.getParam())) {
                String paramString = LimsSqlHelper.replaceParamString(sourceTreeDTO.getParam(), param, rootParameter);
                JSON.parseObject(paramString).forEach(wrapperBean::setFilterValue);
            }

            List<Map<String, Object>> mapList = reportTemplateSourceService.selectListBySql(sql, wrapperBean.extractMapFilter());
            if (!CollectionUtils.isEmpty(mapList)) {
                Map<LimsInterfaceSourceTreeDTO, List<LimsTemplateDataMap<Object>>> childrenDataMap = new HashMap();
                List<AbstractTreeNode> children = sourceTreeDTO.getChildren();
                if (!CollectionUtils.isEmpty(children)) {
                    children.forEach((childNode) -> {
                        LimsInterfaceSourceTreeDTO childSourceDTO = (LimsInterfaceSourceTreeDTO)childNode;
                        MapperParameter childParameter = this.getParamFromParent(mapList, childSourceDTO.getParam(), new MapperParameter());
                        List<LimsTemplateDataMap<Object>> childDataList = this.execute(childSourceDTO, childParameter, new MapperParameter());
                        if (!CollectionUtils.isEmpty(childDataList)) {
                            childrenDataMap.put(childSourceDTO, childDataList);
                        }

                    });
                }

                dataMapList = mapList.stream().map((map) -> {
                    LimsTemplateDataMap<Object> limsDataMap = (LimsTemplateDataMap)map;
                    this.putRelativeChildDataMap(limsDataMap, childrenDataMap);
                    return limsDataMap;
                }).collect(Collectors.toCollection(LimsTemplateDataList::new));
            }
        }

        return dataMapList;
    }

    private MapperParameter getParamFromParent(List<Map<String, Object>> parentDataList, String param, MapperParameter rootParameter) {
        List<String> paramTagList = LimsSqlHelper.getParamTagList(param);
        MapperParameter parameter = new MapperParameter();
        parameter.putAll(rootParameter);
        paramTagList.forEach((paramTag) -> {
            List<Object> paramDataList = parentDataList.stream()
                    .filter((parentData) -> parentData.get(paramTag) != null)
                    .map((parentData) -> parentData.get(paramTag))
                    .distinct().collect(Collectors.toList());
            parameter.put(paramTag, StringUtils.join(paramDataList, ","));
        });
        return parameter;
    }

    private void putRelativeChildDataMap(LimsTemplateDataMap<Object> parentDataMap, Map<LimsInterfaceSourceTreeDTO, List<LimsTemplateDataMap<Object>>> childrenDataMap) {
        childrenDataMap.forEach((childNode, dataList) -> {
            if (!CollectionUtils.isEmpty(dataList)) {
                String childParamString = childNode.getParam();
                List<String> paramTagList = LimsSqlHelper.getParamTagList(childParamString);
                if (!StringUtils.isEmpty(childParamString) && !CollectionUtils.isEmpty(paramTagList)) {
                    String paramString = LimsSqlHelper.replaceParamString(childParamString, parentDataMap, null);
                    JSONObject paramObject = JSON.parseObject(paramString);
                    List<LimsTemplateDataMap<Object>> filterList = dataList.stream().filter((childData) -> paramObject.entrySet().stream().
                            allMatch((entry) -> StringUtils.equals(entry.getValue().toString(), childData.get(StringUtils.split(entry.getKey(), "_")[0]).toString())))
                            .collect(Collectors.toCollection(LimsTemplateDataList::new));
                    if (!CollectionUtils.isEmpty(filterList)) {
                        putParam(parentDataMap, childNode, filterList);
                    }
                } else {
                    parentDataMap.put(childNode.getSourceCode(), dataList);
                }

            }
        });
    }

    private static void putParam(LimsTemplateDataMap<Object> dataMap, LimsInterfaceSourceTreeDTO source, List<LimsTemplateDataMap<Object>> dataMapList) {
        if (!CollectionUtils.isEmpty(dataMapList)) {
            switch(LimsSourceDataTypeEnum.getTypeEnum(source.getSourceType())) {
                case DETAIL:
                    dataMap.put(source.getSourceCode(), dataMapList.get(0));
                    break;
                case LIST:
                    dataMap.put(source.getSourceCode(), dataMapList);
                    break;
                default:
                    dataMap.put(source.getSourceCode(), dataMapList);
            }

        }
    }
}
